package com.xuexiang.xui_lib.component.button.shadowbutton;

import ohos.agp.components.AttrHelper;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.render.Path;
import ohos.agp.utils.Color;
import ohos.agp.utils.RectFloat;
import ohos.app.Context;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;
import ohos.multimodalinput.event.MmiPoint;
import ohos.multimodalinput.event.TouchEvent;

import com.xuexiang.xui_lib.ResourceTable;
import com.xuexiang.xui_lib.util.AttrValue;

import java.util.Timer;
import java.util.TimerTask;

/**
 * 可设置波纹阴影效果的Button
 *
 * @author xuexiang
 * @since 2019/1/14 下午10:11
 */
public class RippleShadowShadowButton extends BaseShadowButton
    implements Component.DrawTask, Component.TouchEventListener, Component.BindStateChangedListener {
    private int mRoundRadius;
    private Color mRippleColor;
    private int mRippleDuration;
    private int mRippleRadius;
    private float pointX, pointY;

    private Paint mRipplePaint;
    private RectFloat mRectF;
    private Path mPath;
    private Timer mTimer;

    private TimerTask mTask;

    private int mRippleAlpha;
    private final static int RIPPLE_ALPHA = 47;

    public RippleShadowShadowButton(Context context) {
        super(context);
    }

    public RippleShadowShadowButton(Context context, AttrSet attrSet) {
        super(context, attrSet, "");
    }

    public RippleShadowShadowButton(Context context, AttrSet attrSet, String styleName) {
        super(context, attrSet, styleName);
    }

    @Override
    protected void init(Context context, AttrSet attrSet) {
        super.init(context, attrSet);
        mRippleColor = AttrValue.get(attrSet, "sb_ripple_color",
            new Color(context.getColor(ResourceTable.Color_default_shadow_button_ripple_color)));
        mRippleAlpha = AttrValue.get(attrSet, "sb_ripple_alpha", RIPPLE_ALPHA);
        mRippleDuration = AttrValue.get(attrSet, "sb_ripple_duration", 1000);
        mShapeType = AttrValue.get(attrSet, "sb_shape_type", 1);
        mRoundRadius = AttrValue.getDimension(attrSet, "sb_radius", AttrHelper.vp2px(2, context));
        mRipplePaint = new Paint();
        mRipplePaint.setColor(mRippleColor);
        mRipplePaint.setAlpha(mRippleAlpha / 255f);
        mRipplePaint.setStyle(Paint.Style.FILL_STYLE);
        mRipplePaint.setAntiAlias(true);
        mPath = new Path();
        mRectF = new RectFloat();
        pointY = pointX = -1;
        addDrawTask(this, DrawTask.BETWEEN_BACKGROUND_AND_CONTENT);
        setTouchEventListener(this);
        setBindStateChangedListener(this);
    }

    @Override
    public void onDraw(Component component, Canvas canvas) {
        super.onDraw(component, canvas);
        if (mRipplePaint == null) {
            return;
        }
        drawFillCircle(canvas);
    }

    @Override
    public boolean onTouchEvent(Component component, TouchEvent touchEvent) {
        if (touchEvent.getAction() == TouchEvent.PRIMARY_POINT_DOWN) {
            MmiPoint mmiPoint = touchEvent.getPointerScreenPosition(0);
            int[] location = component.getLocationOnScreen();
            pointX = mmiPoint.getX() - location[0];
            pointY = mmiPoint.getY() - location[1];
            onStartDrawRipple();
        }
        return true;
    }

    /**
     * Draw ripple effect
     *
     * @param canvas
     */
    private void drawFillCircle(Canvas canvas) {
        if (canvas != null && pointX >= 0 && pointY >= 0) {
            int rbX = getEstimatedWidth();
            int rbY = getEstimatedHeight();
            float xMax = Math.max(pointX, Math.abs(rbX - pointX));
            float yMax = Math.max(pointY, Math.abs(rbY - pointY));
            float longDis = (float) Math.sqrt(xMax * xMax + yMax * yMax);
            if (mRippleRadius > longDis) {
                onCompleteDrawRipple();
                return;
            }
            final float drawSpeed = longDis / mRippleDuration * 35;
            mRippleRadius += drawSpeed;

            canvas.save();
            canvas.drawCircle(pointX, pointY, mRippleRadius, mRipplePaint);
            mPath.reset();
            canvas.clipPath(mPath, Canvas.ClipOp.DIFFERENCE);
            if (mShapeType == 0) {
                mPath.addCircle(rbX >> 1, rbY >> 1, mWidth >> 1, Path.Direction.COUNTER_CLOCK_WISE);
            } else {
                mRectF = new RectFloat(0, 0, mWidth, mHeight);
                mPath.addRoundRect(mRectF, mRoundRadius, mRoundRadius, Path.Direction.COUNTER_CLOCK_WISE);
            }
            canvas.clipPath(mPath, Canvas.ClipOp.DIFFERENCE);
            canvas.restore();
        }
    }

    /**
     * Start draw ripple effect
     */
    private void onStartDrawRipple() {
        onCompleteDrawRipple();
        mTask = new TimerTask() {
            @Override
            public void run() {
                new EventHandler(EventRunner.getMainEventRunner()).postTask(new Runnable() {
                    @Override
                    public void run() {
                        invalidate();
                    }
                });
            }
        };
        mTimer = new Timer();
        mTimer.schedule(mTask, 0, 30);
    }

    /**
     * Stop draw ripple effect
     */
    private void onCompleteDrawRipple() {
        if (mTimer != null) {
            if (mTask != null) {
                mTask.cancel();
            }
            mTimer.cancel();
        }
        mRippleRadius = 0;
    }

    @Override
    public void onComponentBoundToWindow(Component component) {
    }

    @Override
    public void onComponentUnboundFromWindow(Component component) {
        onCompleteDrawRipple();
    }

    public int getRoundRadius() {
        return mRoundRadius;
    }

    public void setRoundRadius(int roundRadius) {
        mRoundRadius = roundRadius;
        invalidate();
    }

    public Color getRippleColor() {
        return mRippleColor;
    }

    public void setRippleColor(Color rippleColor) {
        mRippleColor = rippleColor;
    }

    public int getRippleDuration() {
        return mRippleDuration;
    }

    public void setRippleDuration(int rippleDuration) {
        mRippleDuration = rippleDuration;
    }

    public int getRippleRadius() {
        return mRippleRadius;
    }

    public void setRippleRadius(int rippleRadius) {
        mRippleRadius = rippleRadius;
    }

    public int getRippleAlpha() {
        return mRippleAlpha;
    }

    public void setRippleAlpha(int rippleAlpha) {
        mRippleAlpha = rippleAlpha;
    }
}
